#ifndef QUEST_H_INCLUDED
#define QUEST_H_INCLUDED
#include <iostream>

namespace suppfuncs{
    int read_int(int const a=std::numeric_limits<int>::min()+1, int const b=std::numeric_limits<int>::max()){
        int key;
        bool k;
        std::string temp;
        do{

            std::cin >> key;
            k=std::cin.fail();
            std::cin.clear();
            std::getline(std::cin,temp,'\n');

            if (k || !(key>=a && key<=b) || temp.size()!=0) {
                std::cout << "Hibás, add meg ujra: " << std::endl;
            }
        }while (k || !(key>=a && key<=b) || temp.size()!=0);
        return key;
    }

    std::string PutNewLines(std::string const &val){

        std::string str=val;
        std::size_t found = str.rfind("\\n");
        while(found!=std::string::npos){
            str.replace (found,2,"\n");
            found = str.rfind("\\n");
        }
        return str;
    }


    std::string PutSpaces(std::string const &val){

        std::string str=val;
        std::size_t found = str.rfind('_');
        while(found!=std::string::npos){
            str.replace (found,1," ");
            found = str.rfind('_');
        }
        return str;
    }

}


class Quest{
protected:
    std::string quest_text;
    std::string quest_name;
    int exper;
public:
    std::string  Get_Quest_Text()const{return quest_text;}
    int Get_Xp()const{return exper;}
    std::string Get_QName()const {return quest_name;}
    virtual bool Begin_Quest(Character* hero)=0;
    virtual std::string Get_Next_QName() const =0;
    Quest(std::string name, std::string text, int xp):
        quest_name(name), quest_text(text), exper(xp){}
    ~Quest(){};
};



class Combat: public Quest{
private:
    std::string next_qname;
    Character* enemy;
    int row=0;
public:
    bool Begin_Quest(Character* hero);
    bool Manual(Character* hero);
    bool Auto(Character* hero);
    std::string Get_Next_QName()const {return next_qname;}
    void Display_Status(Character* hero);
    Combat(std::string const &name, std::string const &text, int const &xp, std::string const &nqname, std::string const &enemydata);
    ~Combat(){ delete enemy;}
};

Combat::Combat(std::string const &name, std::string const &text, int const &xp, std::string const &nqname, std::string const &enemydata):
Quest(name,text,xp),next_qname(nqname){

std::stringstream dataline;
std::string tempname, tempstring;
int levelnumber;
        dataline.str("");
        dataline<<enemydata;
        dataline>>tempname;
        dataline>>tempstring;
        dataline>>levelnumber;

        switch(tempstring[0]){
            case 'H': enemy=new Warrior(tempname,levelnumber,0,""); break;
            case 'I': enemy=new Archer(tempname,levelnumber,0,""); break;
            case 'M': enemy=new Mage(tempname,levelnumber,0,""); break;
            case 'N':
                int temp1,temp2,temp3,temp4,temp5,temp6;
                dataline>>temp1>>temp2>>temp3>>temp4>>temp5>>temp6;
                enemy=new Npc(tempname,levelnumber,temp1,temp2,temp3,temp4,temp5,temp6);
        }

}

bool Combat::Begin_Quest(Character* hero){
    std::cout<<"Hogyan szeretned lejatszani a harcot?"<<std::endl;
    std::cout<<"1) Klaszikus harc"<<std::endl;
    std::cout<<"2) Automatikus harc"<<std::endl;
    bool k;
    int key=suppfuncs::read_int(1,2);

    if(key==1)
        k=Manual(hero);
    else if(key==2) k=Auto(hero);
    return k;
}

bool Combat::Auto(Character* hero){
    int P_stunned=0, E_stunned=0;
    row=0;
    const int SB_Size= ("Npc"==enemy->GetCharacter_Cast())? 2:4;
    const int PChance=rand() % 100;

    if(PChance<50){
        std::cout<<"Az ellenfeled kezd."<<std::endl;
        P_stunned=1;
        }else std::cout<<"Te kezdesz!"<<std::endl;

    hero->Reset_Actual_Health();
    enemy->Reset_Actual_Health();



    while(hero->GetActual_Health()>0 && enemy->GetActual_Health()>0){
        if(PChance>=50){
            ++row;
            hero->DecGlobal_CoolDown();
            enemy->DecGlobal_CoolDown();
            std::cout<<"Kor: "<<row<<std::endl;
        }
    //block player
        if(hero->GetActual_Health()>0 && P_stunned==0){
        Display_Status(hero);
            int i;
            for(i=3; !hero->Is_SpellReady(i,enemy);--i);
            switch(i){
                case 0: hero->Use_Attack_One(enemy);
                        E_stunned=hero->Get_StunTime(0);
                        std::cout<<"Ezt hasznaltad: "<<hero->Get_SpellName(1)<<std::endl;
                        break;
                case 1: hero->Use_Attack_Two(enemy);
                        E_stunned=hero->Get_StunTime(1);
                        std::cout<<"Ezt hasznaltad: "<<hero->Get_SpellName(1)<<std::endl;
                        break;
                case 2: hero->Use_Attack_Three(enemy);
                        E_stunned=hero->Get_StunTime(2);
                        std::cout<<"Ezt hasznaltad: "<<hero->Get_SpellName(2)<<std::endl;
                        break;
                case 3: hero->Use_Attack_Four(enemy);
                        E_stunned=hero->Get_StunTime(3);
                        std::cout<<"Ezt hasznaltad: "<<hero->Get_SpellName(3)<<std::endl;
                        break;
            }


        }else if(P_stunned!=0){
            --P_stunned;
            if(row!=0) std::cout<<"Te most kimaradtal. Meg ennyiszer maradsz ki:" <<P_stunned<<std::endl;
        }

    //player block end


        if(PChance<50){
            ++row;
            hero->DecGlobal_CoolDown();
            enemy->DecGlobal_CoolDown();
            std::cout<<"Kor: "<<row<<std::endl;
        }
    //block enemy
        if(enemy->GetActual_Health()>0 && E_stunned==0){
            Display_Status(hero);
            int i;
            for(i=SB_Size-1; !enemy->Is_SpellReady(i,hero);--i);
            switch(i){
                case 0: enemy->Use_Attack_One(hero);
                        P_stunned=enemy->Get_StunTime(0);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(1)<<std::endl;
                        break;
                case 1: enemy->Use_Attack_Two(hero);
                        P_stunned=enemy->Get_StunTime(1);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(1)<<std::endl;
                        break;
                case 2: enemy->Use_Attack_Three(hero);
                        P_stunned=enemy->Get_StunTime(2);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(2)<<std::endl;
                        break;
                case 3: enemy->Use_Attack_Four(hero);
                        P_stunned=enemy->Get_StunTime(3);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(3)<<std::endl;
                        break;
            }
        }else if(E_stunned!=0){
            --E_stunned;
            std::cout<<"Az elenfeled kimarad, te jossz. Meg ennyiszer marad ki:" <<E_stunned<<std::endl;
        }
    //enemy block end
    }
    Display_Status(hero);

    if(hero->GetActual_Health()!=0){
        int damage_div= ((hero->GetHealth()-hero->GetActual_Health())*100/hero->GetHealth())/10 +1;
        hero->Add_Experience(row*Get_Xp()/damage_div);
    }
    return (hero->GetActual_Health()!=0);
}

bool Combat::Manual(Character* hero){
    int P_stunned=0, E_stunned=0;
    int key;
    row=0;
    const int SB_Size= ("Npc"==enemy->GetCharacter_Cast())? 2:4;
    const int PChance=rand() % 100;

    if(PChance<50){
        std::cout<<"Az ellenfeled kezd."<<std::endl;
        P_stunned=1;
        }else std::cout<<"Te kezdesz!"<<std::endl;

    hero->Reset_Actual_Health();
    enemy->Reset_Actual_Health();



    while(hero->GetActual_Health()>0 && enemy->GetActual_Health()>0){
        if(PChance>=50){
            ++row;
            hero->DecGlobal_CoolDown();
            enemy->DecGlobal_CoolDown();
            std::cout<<"Kor: "<<row<<std::endl;
        }
    //block player
        if(hero->GetActual_Health()>0 && P_stunned==0){
            Display_Status(hero);
         //itt csinálok valamit
            std::cout<<"Valasz tamadast:"<<std::endl;
            for(int i=0;i<4;++i){
                std::cout<<i+1<<") "<< hero->Get_SpellName(i).c_str();
                if(hero->Is_SpellReady(i,enemy)) {std::cout<<std::endl<<"   Tipp: "<<hero->Get_SpellTip(i).c_str()<<std::endl;}
                else std::cout<<"- Jelenleg nem hasznalhato."<<std::endl;
            }

        // Readkey
            do{
                key=suppfuncs::read_int(1,4)-1;
                if(!hero->Is_SpellReady(key, enemy)) std::cout<<"Ez nem hasznalhato, valasz masikat: ";
            }while(!hero->Is_SpellReady(key, enemy));

        //spell switch
            switch(key){
                case 0: hero->Use_Attack_One(enemy); E_stunned=hero->Get_StunTime(0); break;
                case 1: hero->Use_Attack_Two(enemy); E_stunned=hero->Get_StunTime(1); break;
                case 2: hero->Use_Attack_Three(enemy); E_stunned=hero->Get_StunTime(2); break;
                case 3: hero->Use_Attack_Four(enemy); E_stunned=hero->Get_StunTime(3); break;
            }

        }else if(P_stunned!=0){
            --P_stunned;
            if(row!=0) std::cout<<"Te most kimaradtal. Meg ennyiszer maradsz ki:" <<P_stunned<<std::endl;
        }

    //player block end


        if(PChance<50){
            ++row;
            hero->DecGlobal_CoolDown();
            enemy->DecGlobal_CoolDown();
            std::cout<<"Kor: "<<row<<std::endl;
        }

    //block enemy
        if(enemy->GetActual_Health()>0 && E_stunned==0){
            Display_Status(hero);
            int i;
            for(i=SB_Size-1; !enemy->Is_SpellReady(i,hero);--i);
            switch(i){
                case 0: enemy->Use_Attack_One(hero);
                        P_stunned=enemy->Get_StunTime(0);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(0)<<std::endl;
                        break;
                case 1: enemy->Use_Attack_Two(hero);
                        P_stunned=enemy->Get_StunTime(1);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(1)<<std::endl;
                        break;
                case 2: enemy->Use_Attack_Three(hero);
                        P_stunned=enemy->Get_StunTime(2);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(2)<<std::endl;
                        break;
                case 3: enemy->Use_Attack_Four(hero);
                        P_stunned=enemy->Get_StunTime(3);
                        std::cout<<"Az elenfeled ezt hasznalta: "<<enemy->Get_SpellName(3)<<std::endl;
                        break;
            }
        }else if(E_stunned!=0){
            --E_stunned;
            std::cout<<"Az elenfeled kimarad, te jossz. Meg ennyiszer marad ki:" <<E_stunned<<std::endl;
        }
    //enemy block end
    }
    Display_Status(hero);
    if(hero->GetActual_Health()!=0){
        int damage_div= ((hero->GetHealth()-hero->GetActual_Health())*100/hero->GetHealth())/10 +1;
        hero->Add_Experience(row*Get_Xp()/damage_div);
    }
    return (hero->GetActual_Health()!=0);
}


void Combat::Display_Status(Character* hero){
    std::cout<<std::endl;
    std::cout<<std::endl;
    std::cout<<hero->GetName()<<" ("<<hero->GetLevel()<<")"<<std::endl;
    std::cout<<"HP: "<< hero->GetHealth()<<"/"<<hero->GetActual_Health()<<std::endl;
    std::cout<<"DMG: "<<hero->GetMax_Damage()<<" - "<<hero->GetMin_Damage()<<std::endl;
    std::cout<<"-^--^--^--^--^--^-"<<std::endl;
    std::cout<<enemy->GetName()<<" ("<<enemy->GetLevel()<<")"<<std::endl;
    std::cout<<"HP: "<< enemy->GetHealth()<<"/"<<enemy->GetActual_Health()<<std::endl;
    std::cout<<"DMG: "<<enemy->GetMax_Damage()<<" - "<<enemy->GetMin_Damage()<<std::endl;
    std::cout<<std::endl;
    std::cout<<std::endl;
}


class CrossRoad:public Quest{
private:
    std::vector<std::string> next_quest;
    int choosen;
public:
    void Choose();
    bool Begin_Quest(Character* hero);
    std::string Get_Next_QName()const {return next_quest[choosen];}
    CrossRoad(const std::string  &name, const std::string &text,const std::string  &nextQuestline);
};

CrossRoad::CrossRoad(const std::string  &name, const std::string &text,const std::string  &nextQuestline):Quest(name, text, 0){
    std::stringstream dataline;
    std::string tempstring;

    dataline.str("");
    dataline<<nextQuestline;
    dataline>>tempstring;
    next_quest.push_back(tempstring);

    while(!dataline.eof()){
        dataline>>tempstring;
        next_quest.push_back(tempstring);
    }
}

void CrossRoad::Choose(){
    choosen=suppfuncs::read_int(1,next_quest.size())-1;
}


bool CrossRoad::Begin_Quest(Character* hero){
Choose();
return true;
}


class LogicQuest: public Quest{
private:
    int questanswer, ansinterw, choosen;
    std::vector<std::string> next_quest;

public:
    std::string Get_Next_QName()const {return next_quest[choosen];}
    int Give_ans();
    bool Begin_Quest(Character* hero);
    LogicQuest(const std::string  &name, const std::string &text, const int &xp, const int &ans, const int &ansint , const std::string  &nextQuestline);

};


int LogicQuest::Give_ans(){
    int answer, i=0;
    do{
    ++i;
    answer=suppfuncs::read_int();
    if(answer<questanswer-ansinterw || answer>questanswer+ansinterw) std::cout<<"Nem ez a helyes valas, probalkozz ujra."<<std::endl;
    }while((answer<questanswer-ansinterw || answer>questanswer+ansinterw) && i<3);
    return answer;
}

bool LogicQuest::Begin_Quest(Character* hero){
    int answer=Give_ans();
    int xpminus;
    if(!(answer<questanswer-ansinterw || answer>questanswer+ansinterw)){
        choosen=0;
        xpminus=questanswer-answer;
        if(xpminus<0) xpminus=(-1)*xpminus;
        hero->Add_Experience(Get_Xp()-xpminus);
        return true;
    }else if(next_quest.size()==2){
            choosen=1;
            return true;
            }else return false;
}

LogicQuest::LogicQuest(const std::string  &name, const std::string &text, const int &xp, const int &ans, const int &ansint , const std::string  &nextQuestline):
    Quest(name,text,xp), questanswer(ans), ansinterw(ansint){

    std::stringstream dataline;
    std::string tempstring;

    dataline.str("");
    dataline<<nextQuestline;
    dataline>>tempstring;
    next_quest.push_back(tempstring);

    while(!dataline.eof()){
        dataline>>tempstring;
        next_quest.push_back(tempstring);
    }
}


#endif // QUEST_H_INCLUDED
